鸿蒙内核源码分析(内存映射篇)

您所在的位置:网站首页 鸿蒙内核架构 l1 鸿蒙内核源码分析(内存映射篇)

鸿蒙内核源码分析(内存映射篇)

2024-07-10 04:44| 来源: 网络整理| 查看: 265

MMU的本质

虚拟地址(VA): 就是线性地址, 鸿蒙内存部分全是VA的身影, 是由编译器和链接器在定位程序时分配的,每个应用程序都使用相同的虚拟内存地址空间,而这些虚拟内存地址空间实际上分别映射到不同的实际物理内存空间上。CPU只知道虚拟地址,向虚拟地址要数据,但在其保护模式下很悲催地址信号在路上被MMU拦截了,MMU把虚拟地址换成了物理地址,从而拿到了真正的数据。

物理地址(PA):程序的指令和常量数据,全局变量数据以及运行时动态申请内存所分配的实际物理内存存放位置。

MMU采用页表(page table)来实现虚实地址转换,页表项除了描述虚拟页到物理页直接的转换外,还提供了页的访问权限(读,写,可执行)和存储属性。 MMU的本质是拿虚拟地址的高位(20位)做文章,低12位是页内偏移地址不会变。也就是说虚拟地址和物理地址的低12位是一样的,本篇详细讲述MMU是如何变戏法的。

MMU是通过两级页表结构:L1和L2来实现映射功能的,鸿蒙内核当然也实现了这两级页表转换的实现。本篇是系列篇关于内存部分最满意的一篇,也是最不好理解的一篇, 强烈建议结合源码看.鸿蒙内核源码注释 < G| G| C| C >

一级页表L1

L1页表将全部的4G地址空间划分为4096个1M的节,页表中每一项(页表项)32位,其内容是L2页表基地址或某个1M物理内存的基地址。虚拟地址的高12位用于对页表项定位,也就是4096个页面项的索引,L1页表的基地址,也叫转换表基地址,存放在CP15的C2(TTB)寄存器中,鸿蒙内核源码分析(内存汇编篇)中有详细的描述,自行翻看。

L1页表项有三种描述格式,鸿蒙源码如下。

/* L1 descriptor type */ #define MMU_DESCRIPTOR_L1_TYPE_INVALID                          (0x0 > MMU_DESCRIPTOR_L2_SMALL_SHIFT)) {         VM_ERR("mmap failed, status: %d", status);         return;     }     length = sizeof(mmuKernelMappings) / sizeof(LosArchMmuInitMapping);     for (i = 0; i archMmu, kernelMap->virt, kernelMap->phys,                                  kernelMap->size >> MMU_DESCRIPTOR_L2_SMALL_SHIFT, kernelMap->flags);         if (status != (kernelMap->size >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) {             VM_ERR("mmap failed, status: %d", status);             return;         }         LOS_VmSpaceReserve(kSpace, kernelMap->size, kernelMap->virt);//保留区     }  //将剩余空间映射好     kmallocLength = KERNEL_VMM_BASE + SYS_MEM_SIZE_DEFAULT - bssEndBoundary;     status = LOS_ArchMmuMap(&kSpace->archMmu, bssEndBoundary,                              SYS_MEM_BASE + bssEndBoundary - KERNEL_VMM_BASE,                              kmallocLength >> MMU_DESCRIPTOR_L2_SMALL_SHIFT,                              VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE);     if (status != (kmallocLength >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) {         VM_ERR("unmap failed, status: %d", status);         return;     }     LOS_VmSpaceReserve(kSpace, kmallocLength, bssEndBoundary);     /* we need free tmp ttbase */     oldTtPhyBase = OsArmReadTtbr0();//读取TTB值     oldTtPhyBase = oldTtPhyBase & MMU_DESCRIPTOR_L2_SMALL_FRAME;     OsArmWriteTtbr0(kSpace->archMmu.physTtb | MMU_TTBRx_FLAGS);//内核页表基地址写入CP15 c2(TTB寄存器)     ISB;     /* we changed page table entry, so we need to clean TLB here */     OsCleanTLB();//清空TLB缓冲区     (VOID)LOS_MemFree(m_aucSysMem0, (VOID *)(UINTPTR)(oldTtPhyBase - SYS_MEM_BASE + KERNEL_VMM_BASE));//释放内存池 }

 

LOS_ArchMmuMap

mmu的map 就是生成L1,L2页表项的过程,以供虚实地址的转换使用,还是直接看代码吧,代码说明一切!

//所谓的 map 就是 生成L1,L2页表项的过程 status_t LOS_ArchMmuMap(LosArchMmu *archMmu, VADDR_T vaddr, PADDR_T paddr, size_t count, UINT32 flags) {     PTE_T l1Entry;     UINT32 saveCounts = 0;     INT32 mapped = 0;     INT32 checkRst;     checkRst = OsMapParamCheck(flags, vaddr, paddr);//检查参数     if (checkRst  0) {         if (MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(vaddr) && //虚拟地址和物理地址对齐 0x100000(1M)时采用             MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(paddr) && //section页表项格式             count >= MMU_DESCRIPTOR_L2_NUMBERS_PER_L1) { //MMU_DESCRIPTOR_L2_NUMBERS_PER_L1 = 0x100              /* compute the arch flags for L1 sections cache, r ,w ,x, domain and type */             saveCounts = OsMapSection(archMmu, flags, &vaddr, &paddr, &count);//生成L1 section类型页表项并保存         } else {             /* have to use a L2 mapping, we only allocate 4KB for L1, support 0 ~ 1GB */             l1Entry = OsGetPte1(archMmu->virtTtb, vaddr);//获取L1页面项             if (OsIsPte1Invalid(l1Entry)) {//L1 fault页面项类型                 OsMapL1PTE(archMmu, &l1Entry, vaddr, flags);//生成L1 page table类型页表项并保存                 saveCounts = OsMapL2PageContinous(l1Entry, flags, &vaddr, &paddr, &count);//生成L2 页表项目并保存             } else if (OsIsPte1PageTable(l1Entry)) {//L1 page table页面项类型                 saveCounts = OsMapL2PageContinous(l1Entry, flags, &vaddr, &paddr, &count);//生成L2 页表项目并保存             } else {                 LOS_Panic("%s %d, unimplemented tt_entry %x\n", __FUNCTION__, __LINE__, l1Entry);             }         }         mapped += saveCounts;     }     return mapped; } STATIC UINT32 OsMapL2PageContinous(PTE_T pte1, UINT32 flags, VADDR_T *vaddr, PADDR_T *paddr, UINT32 *count) {     PTE_T *pte2BasePtr = NULL;     UINT32 archFlags;     UINT32 saveCounts;     pte2BasePtr = OsGetPte2BasePtr(pte1);     if (pte2BasePtr == NULL) {         LOS_Panic("%s %d, pte1 %#x error\n", __FUNCTION__, __LINE__, pte1);     }     /* compute the arch flags for L2 4K pages */     archFlags = OsCvtPte2FlagsToAttrs(flags);     saveCounts = OsSavePte2Continuous(pte2BasePtr, OsGetPte2Index(*vaddr), *paddr | archFlags, *count);     *paddr += (saveCounts > 提交代码注解 >> 新建 Pull Request

新建 Issue

 

鸿蒙内核源码分析(内存映射篇) | 虚拟地址-物理地址是如何映射的 | 百篇博客分析HarmonyOS源码 | v15.02_HarmonyOS_06



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3